﻿using System;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
using Skyline;

namespace QuantBox
{
    internal static class Helper
    {
        private static DateTime ParseDate(JToken? token, string format)
        {
            var value = token?.TryGetValue<string>();
            DateTime.TryParseExact(value, format, null, DateTimeStyles.None, out var date);
            return date;
        }

        public static T TryGetValue<T>(this JToken? token, T defaultValue = default)
        {
            if (token == null) {
                return defaultValue!;
            }

            try {
                return token.ToObject<T>()!;
            }
            catch {
                return defaultValue!;
            }
        }

        public static InstrumentData? ReadOption(JToken token)
        {
            try {
                var data = new InstrumentData();
                data.Id = TryGetValue<string>(token["wind_code"]);
                data.Symbol = token["sec_name"]!.TryGetValue<string>();
                data.Exchange = token["exchanges"]!.TryGetValue<string>();
                data.ExchangeCode = token["exchange_code"]?.TryGetValue<string>();
                data.UnderlyingId = token["option_mark_code"]!.TryGetValue<string>();
                data.Type = token["option_type"]!.TryGetValue<string>();
                data.CallOrPut = token["call_or_put"]!.TryGetValue<string>();
                data.SpecialType = token["exercise_mode"]!.TryGetValue<string>();
                data.RoundLot = token["exercise_price"]!.TryGetValue<double>();
                data.Factor = token["contract_unit"]!.TryGetValue<double>();
                data.ListedDate = ParseDate(token["listed_date"], "yyyy-MM-dd");
                data.MaturityDate = ParseDate(token["expire_date"], "yyyy-MM-dd");
                data.ExerciseDate = ParseDate(token["exercise_date"], "yyyy-MM-dd");
                data.SettlementDate = ParseDate(token["settlement_date"], "yyyy-MM-dd");
                data.SettlementMethod = token["settle_mode"]!.TryGetValue<string>();
                data.Status = token["contract_state"]!.TryGetValue<string>();
                data.TickSize = token["tick_size"]!.TryGetValue<double>();
                SetId(data);
                return data;
            }
            catch {
                Console.WriteLine(token.ToString());
                return null;
            }

            static void SetId(InstrumentData data)
            {
                if (data.Exchange == "SSE" || data.Exchange == "SZSE") {
                    var match = Regex.Match(data.UnderlyingId ?? string.Empty, @"\d+");
                    if (match.Success) {
                        var id = new StringBuilder();
                        id.Append(match.Value);
                        id.Append(data.CallOrPut == "认购" ? "C" : "P");
                        id.Append(data.MaturityDate.ToString("yyMM"));
                        id.Append("M");
                        id.Append(((int)(data.RoundLot * Math.Pow(10, Utility.GetPrecision(data.TickSize)))).ToString("D5"));
                        data.Id = id.ToString();
                    }
                }
            }
        }

        public static InstrumentData? ReadStock(JToken token)
        {
            try {
                var data = new InstrumentData();
                data.Id = token["order_book_id"]!.TryGetValue<string>();
                data.ExchangeCode = token["exchange_code"]?.TryGetValue<string>();
                data.Symbol = token["symbol"]!.TryGetValue<string>();
                data.Abbrev = token["abbrev_symbol"]!.TryGetValue<string>();
                data.RoundLot = token["round_lot"]!.TryGetValue<int>();
                data.SectorCode = token["sector_code"]?.TryGetValue<string>();
                data.SectorName = token["sector_code_name"]?.TryGetValue<string>();
                data.IndustryCode = token["industry_code"]?.TryGetValue<string>();
                data.Industry = token["industry_name"]?.TryGetValue<string>();
                data.ListedDate = DateTime.ParseExact(token["listed_date"]!.TryGetValue<string>(), "yyyyMMdd", null);
                data.MaturityDate = DateTime.ParseExact(token["de_listed_date"]!.TryGetValue<string>(), "yyyyMMdd", null);
                data.Type = token["type"]!.TryGetValue<string>();
                data.Concept = token["concept_names"]?.TryGetValue<string>();
                data.Exchange = token["exchange"]!.TryGetValue<string>();
                data.Board = token["board_type"]?.TryGetValue<string>();
                data.Status = token["status"]?.TryGetValue<string>();
                data.SpecialType = token["special_type"]?.TryGetValue<string>();
                data.TickSize = token["tick_size"]!.TryGetValue<double>();
                return data;
            }
            catch {
                Console.WriteLine(token.ToString());
                return null;
            }
        }

        public static InstrumentData ReadFuture(JToken token, InstrumentData data)
        {
            data.DeliveryMonth = token["dlmonth"]!.TryGetValue<string>();
            if (DateTime.TryParseExact(
                token["lastdelivery_date"]!.TryGetValue<string>(), 
                "yyyyMMdd", 
                null,
                DateTimeStyles.None, 
                out var lastDeliveryDate))
            {
                data.LastDeliveryDate = lastDeliveryDate;
            }
            else {
                data.LastDeliveryDate = data.MaturityDate;
            }
            return data;
        }

        public static InstrumentData? ReadFuture2(JToken token)
        {
            try {
                var data = new InstrumentData();
                data.Id = token["order_book_id"]!.TryGetValue<string>();
                data.Symbol = token["symbol"]!.TryGetValue<string>();
                data.MarginRate = token["margin_rate"]!.TryGetValue<double>();
                data.Abbrev = token["abbrev_symbol"]!.TryGetValue<string>();
                data.RoundLot = token["round_lot"]!.TryGetValue<int>();
                data.ListedDate = DateTime.ParseExact(token["listed_date"]!.TryGetValue<string>(), "yyyyMMdd", null);
                data.Type = token["type"]!.TryGetValue<string>();
                data.Factor = token["contract_multiplier"]!.TryGetValue<double>();
                data.UnderlyingId = token["underlying_order_book_id"]!.TryGetValue<string>();
                data.UnderlyingSymbol = token["underlying_symbol"]!.TryGetValue<string>();
                data.MaturityDate = DateTime.ParseExact(token["maturity_date"]!.TryGetValue<string>(), "yyyyMMdd", null);
                data.SettlementMethod = token["settlement_method"]!.TryGetValue<string>();
                data.Product = token["product"]!.TryGetValue<string>();
                data.Exchange = token["exchange"]!.TryGetValue<string>();
                data.ExchangeCode = token["exchange_code"]!.TryGetValue<string>();
                data.TickSize = token["tick_size"]!.TryGetValue<double>();
                return data;
            }
            catch {
                Console.WriteLine(token.ToString());
                return null;
            }
        }

        public static InstrumentData? ReadFuture1(JToken token)
        {
            try {
                var data = new InstrumentData();
                data.Id = token["instrument"]!.TryGetValue<string>();
                data.Exchange = token["exch_eng"]!.TryGetValue<string>();
                data.Symbol = token["sccode"]!.TryGetValue<string>();
                data.MarginRate = token["margin"]!.TryGetValue<double>();
                data.RoundLot = 0;
                data.ListedDate = DateTime.ParseExact(token["ipo_date"]!.TryGetValue<string>(), "yyyyMMdd", null);
                data.Type = DataCenterConst.Future;
                data.Factor = token["contractmultiplier"]!.TryGetValue<double>();
                //data.UnderlyingId = token["underlying_order_book_id"].TryGetValue<string>();
                //data.UnderlyingSymbol = token["underlying_symbol"].TryGetValue<string>();
                data.MaturityDate = DateTime.ParseExact(token["lasttrade_date"]!.TryGetValue<string>(), "yyyyMMdd", null);
                data.Product = Regex.Match(data.Id, "[a-zA-Z]+").Value;
                data.ExchangeCode = token["sec_name"]!.TryGetValue<string>();
                //var mfprice = token["mfprice"].TryGetValue<string>();
                //data.TickSize = double.Parse(Regex.Match(mfprice, @"[\.0-9]+").Value);
                data.TickSize = token["changelt"]!.TryGetValue<double>();
                return data;
            }
            catch {
                Console.WriteLine(token.ToString());
                return null;
            }
        }

        public static InstrumentData? ReadFuture(JToken token)
        {
            return token["order_book_id"] != null ? ReadFuture2(token) : ReadFuture1(token);
        }
    }
}